#pragma once
#ifndef __UDP_HPP__

#include <stdlib.h>
#include <errno.h>
#include <stdio.h> // Standard input/output definitions
#include <fcntl.h> // File control definitions
#include <signal.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <functional>
#include <string>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>

#ifdef __linux__
#include <unistd.h> // UNIX standard function definitions
#include <sys/ioctl.h>
#include <pthread.h> // This uses POSIX Threads
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif //
#ifdef _WIN32
#include <winsock2.h>
#include <Ws2tcpip.h>
#endif

namespace udp_proxy{

constexpr ssize_t RECEIVE_BUF_MAX_SIZE = 512;

class ConnUdp{
public:
    using received_cb = std::function<void(uint8_t *buf, size_t len)>;

    static constexpr auto DEFAULT_BIND_HOST   = "127.0.0.1";
    static constexpr auto DEFAULT_BIND_PORT   = 14550;
    static constexpr auto DEFAULT_REMOTE_HOST = "";
    static constexpr auto DEFAULT_REMOTE_PORT = 14550;
    ConnUdp(std::string bind_host = DEFAULT_BIND_HOST, uint16_t bind_port = DEFAULT_BIND_PORT,
               std::string remote_host = DEFAULT_REMOTE_HOST, uint16_t remote_port = DEFAULT_REMOTE_PORT);
    ~ConnUdp();

    int connect(const received_cb cb);

    int  open();
    void close();

    size_t receive_data(uint8_t *buf);
    size_t send_data(uint8_t *buf, size_t len);

    bool is_open();

protected:
    //
    bool _setup();
    void _thread_recv_callback();

    bool _should_exit;
    std::thread _thread_recv;
    bool _isopen{false};
    std::mutex  _tx_mutex;

    received_cb _received_cb;

private:

#if __linux__
    int                _socket;
    struct sockaddr_in _dest_addr;
    struct sockaddr_in _loc_addr;
    //
    pthread_mutex_t _lock;
#elif _WIN32
    SOCKET      _socket;
    SOCKADDR_IN _dest_addr;
    SOCKADDR_IN _loc_addr;
#endif //
    std::string _local_host;
    uint16_t    _local_port;
    std::string _remote_host;
    uint16_t    _remote_port;
    //
    char _buf_recv[RECEIVE_BUF_MAX_SIZE];
    int  _buf_ptr;
    int  _buf_len;
};
}

#endif // __UDP_HPP__